package tcpudp

import (
	"time"

	"github.com/project-flogo/core/support/log"
)

type stxFixedLenParserConfig struct {
	STX         byte          `json:"startByte,omitempty"`
	Len         int           `json:"len,omitempty"`
	FeedTimeout time.Duration `json:"feedTimeout,omitempty"`
	logger      log.Logger
}

type stxFixedLenParser struct {
	remains []framer
	config  stxFixedLenParserConfig
	logger  log.Logger
}

func NewSTXFixedLenParser(config stxFixedLenParserConfig) (*stxFixedLenParser, error) {
	return &stxFixedLenParser{
		remains: []framer{},
		config:  config,
		logger:  config.logger,
	}, nil
}

func (p *stxFixedLenParser) Feed(data []byte) ([]Packet, error) {
	p.logger.Infof("feed with data: %v", data)
	now := time.Now()
	nFrames := []framer{}
	for _, f := range p.remains {
		if f.deadline.IsZero() || f.deadline.After(now) {
			nFrames = append(nFrames, f)
		}
	}
	res, err := p.parse(nFrames, time.Now(), data)
	if err != nil {
		p.logger.Errorf("failed parse data")
	}
	p.remains = res.remains
	packets := makePackets(res.frames)
	return packets, nil
}

func (p *stxFixedLenParser) Reset() {

}

func (p *stxFixedLenParser) parse(initialFramers []framer, now time.Time, data []byte) (parseResult, error) {
	compFrames := []framer{}
	frames := initialFramers
	for _, b := range data {
		if b == p.config.STX {
			deadline := time.Time{}
			if p.config.FeedTimeout > 0 {
				d := now.Add(p.config.FeedTimeout)
				deadline = d
			}
			f, _ := newFramer(p.config.Len, deadline)
			frames = append(frames, f)
		}
		compIndex := -1
		for idx := range frames {
			c := frames[idx].feed(b)
			if c {
				compFrames = append(compFrames, frames[idx])
				compIndex = idx
			}
		}
		if compIndex >= 0 {
			frames = frames[compIndex+1:]
		}
	}
	res := parseResult{
		frames:  compFrames,
		remains: frames,
	}
	return res, nil
}

func makePackets(frames []framer) []Packet {
	packets := make([]Packet, len(frames))
	for idx, f := range frames {
		p := Packet{
			Framer:  FRAMER_STX_FIXED_LEN,
			Payload: f.data,
		}
		packets[idx] = p
	}
	return packets
}

type parseResult struct {
	frames  []framer
	remains []framer
}

type framer struct {
	data     []byte
	size     int
	deadline time.Time
}

func newFramer(size int, deadline time.Time) (framer, error) {
	f := framer{
		data:     []byte{},
		size:     size,
		deadline: deadline,
	}
	return f, nil
}

func (fr *framer) feed(b byte) bool {
	if len(fr.data) < fr.size {
		fr.data = append(fr.data, b)
	}
	return len(fr.data) == fr.size
}

// func (p *stxFixedLenParser)
